home *** CD-ROM | disk | FTP | other *** search
/ Qu.......ke Neue Level / KroGer Software GmbH - Qu_ke.iso / UTILITY / PRG8.ZIP / F_WAD.C < prev    next >
C/C++ Source or Header  |  1996-03-01  |  11KB  |  399 lines

  1. /*
  2.  * Copyright (C) 1996 by Raphael Quinet.  All rights reserved.
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software and
  5.  * its documentation for any purpose and without fee is hereby
  6.  * granted, provided that the above copyright notice appear in all
  7.  * copies and that both that copyright notice and this permission
  8.  * notice appear in supporting documentation.  If more than a few
  9.  * lines of this code are used in a program which displays a copyright
  10.  * notice or credit notice, the following acknowledgment must also be
  11.  * displayed on the same screen: "This product includes software
  12.  * developed by Raphael Quinet for use in the Quake Editing Utilities
  13.  * project."  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR
  14.  * IMPLIED WARRANTY.
  15.  *
  16.  * More information about the QEU project can be found on the WWW:
  17.  * "http://www.montefiore.ulg.ac.be/~quinet/games/editing.html" or by
  18.  * mail: Raphael Quinet, 9 rue des Martyrs, B-4550 Nandrin, Belgium.
  19.  */
  20.  
  21. /*
  22.  * F_WAD.C - Read and write Doom/Heretic/Hexen WAD files.
  23.  */
  24.  
  25. #include "qeu.h"
  26. #include "q_misc.h"
  27. #include "q_files.h"
  28. #include "f_wad.h"
  29.  
  30. /*
  31.  * Read the WAD directory into memory.  The optional offset to the
  32.  * start of the WAD file is given in "offset".  The number of entries in
  33.  * the directory is returned in *dirsize_r.
  34.  */
  35. WADDirPtr ReadWADDirectory(FILE *wadfile, UInt32 offset, UInt16 *dirsize_r)
  36. {
  37.   WADDirPtr  dir;
  38.   UInt32     pos, size;
  39.   UInt16     max, i;
  40.   int        ftype;
  41.  
  42.   *dirsize_r = 0;
  43.   if (wadfile == NULL)
  44.     return NULL;
  45.   if ((fseek(wadfile, offset, SEEK_SET) < 0)
  46.       || ((ftype = ReadMagic(wadfile)) != FTYPE_PWAD && ftype != FTYPE_IWAD)
  47.       || (ReadInt32(wadfile, &size) == FALSE)
  48.       || (ReadInt32(wadfile, &pos) == FALSE)
  49.       || (size == 0L)
  50.       || (size > 65535L)
  51.       || (fseek(wadfile, pos, SEEK_SET) < 0))
  52.     return NULL;
  53.   dir = (WADDirPtr)QMalloc(size * sizeof(struct WADDirectory));
  54.   max = (UInt16)size;
  55.   for (i = 0; i < max; i++)
  56.     {
  57.       if (ReadBytes(wadfile, &dir[i], sizeof(struct WADDirectory)) == FALSE)
  58.     {
  59.       QFree(dir);
  60.       return NULL;
  61.     }
  62.       dir[i].offset = SwapInt32(dir[i].offset);
  63.       dir[i].size = SwapInt32(dir[i].size);
  64.     }
  65.   *dirsize_r = max;
  66.   return dir;
  67. }
  68.  
  69.  
  70. /*
  71.  * Return the index number of the first entry matching "entryname".
  72.  * A number greater or equal to dirsize is returned if no match is found.
  73.  */
  74. UInt16 FindWADEntry(WADDirPtr dir, UInt16 dirsize, char *entryname)
  75. {
  76.   UInt16 i;
  77.  
  78.   if (dir == NULL)
  79.     ProgError("BUG: Cannot find entry in NULL directory");
  80.   for (i = 0; i < dirsize; i++)
  81.     if (!strnicmp(dir[i].name, entryname, 8))
  82.       return i;
  83.   return i;
  84. }
  85.  
  86.  
  87. /*
  88.  * Print the contents of the WAD directory in "outf".
  89.  */
  90. void DumpWADDirectory(FILE *outf, WADDirPtr dir, UInt16 dirsize)
  91. {
  92.   UInt16 i;
  93.   UInt32 sum;
  94.   char   buf[9];
  95.  
  96.   if (outf == NULL || dir == NULL || dirsize == 0)
  97.     return;
  98.   fprintf(outf, " num    offset     size    entry name\n");
  99.   fprintf(outf, "        (hex)      (dec)\n");
  100.   sum = 0L;
  101.   for (i = 0; i < dirsize; i++)
  102.     {
  103.       strncpy(buf, dir[i].name, 8);
  104.       buf[8] = '\0';
  105.       fprintf(outf, "%4u  0x%08lx  %6ld   %s\n",
  106.               i, dir[i].offset, dir[i].size, buf);
  107.       sum += dir[i].size;
  108.     }
  109.   fprintf(outf, "\nTotal size for %4u entries: %7lu bytes.\n", dirsize, sum);
  110.   fprintf(outf, "Size of the WAD directory:   %7lu bytes.\n",
  111.       (UInt32)dirsize * (UInt32)sizeof(struct WADDirectory));
  112.   fprintf(outf, "Total (header + data + dir): %7lu bytes.\n",
  113.       12L + sum + (UInt32)dirsize * (UInt32)sizeof(struct WADDirectory));
  114. }
  115.  
  116.  
  117. /*
  118.  * If "entrynum" is smaller than dirsize, extract the corresponding
  119.  * entry from a WAD file.  Otherwise, extract all entries and save
  120.  * them in separate files.  The files will be saved in the directory
  121.  * "prefixpath".  If "outf" is not null, progress information will be
  122.  * printed in it.
  123.  */
  124. Bool UnWADFile(FILE *outf, FILE *wadfile, UInt32 offset, WADDirPtr dir,
  125.            UInt16 dirsize, UInt16 entrynum, char *prefixpath)
  126. {
  127.   char   *newname;
  128.   char   *p, *q;
  129.   FILE   *newfile;
  130.   FILE   *indexfile;
  131.   UInt16  i, n;
  132.   UInt32  sum;
  133.   Bool    inmap = FALSE;
  134.   int     indir = 0;
  135.  
  136.   if (wadfile == NULL || dir == NULL || dirsize == 0)
  137.     return FALSE;
  138.   if (prefixpath == NULL)
  139.     prefixpath = ".";
  140.   newname = (char *)QMalloc(strlen(prefixpath) + 2 * 9 + 8 + 4 + 2);
  141.   strcpy(newname, prefixpath);
  142.   p = &newname[strlen(newname) - 1];
  143. #ifdef QEU_DOS
  144.   if (*p != '\\')
  145.     {
  146.       p++;
  147.       *p = '\\';
  148.     }
  149. #else
  150.   if (*p != '/')
  151.     {
  152.       p++;
  153.       *p = '/';
  154.     }
  155. #endif
  156.   p++;
  157.   q = p;
  158.   strcpy(p, QEU_INDEX_FILE);
  159.   if (outf != NULL)
  160.     fprintf(outf, "Creating index file %s\n", newname);
  161.   CreatePathToFile(newname);
  162.   indexfile = fopen(newname, "a");
  163.   fprintf(indexfile, "BEGIN WAD\n");
  164.   sum = 0L;
  165.   n = 0;
  166.   for (i = 0; i < dirsize; i++)
  167.     {
  168.       if ((inmap == TRUE)
  169.       && strnicmp(dir[i].name, "THINGS", 6)
  170.       && strnicmp(dir[i].name, "LINEDEFS", 8)
  171.       && strnicmp(dir[i].name, "SIDEDEFS", 8)
  172.       && strnicmp(dir[i].name, "VERTEXES", 8)
  173.       && strnicmp(dir[i].name, "SEGS", 4)
  174.       && strnicmp(dir[i].name, "SSECTORS", 8)
  175.       && strnicmp(dir[i].name, "NODES", 5)
  176.       && strnicmp(dir[i].name, "SECTORS", 7)
  177.       && strnicmp(dir[i].name, "REJECT", 6)
  178.       && strnicmp(dir[i].name, "BLOCKMAP", 8))
  179.     {
  180.       inmap = FALSE;
  181.       p--;
  182. #ifdef QEU_DOS
  183.       while (*(p - 1) != '\\')
  184.         p--;
  185. #else
  186.       while (*(p - 1) != '/')
  187.         p--;
  188. #endif
  189.     }
  190.       if (!strncmp(dir[i].name + 1, "_START", 7)
  191.       || !strncmp(dir[i].name + 2, "_START", 6))
  192.     {
  193.       indir++;
  194.       if (indir > 2)
  195.         {
  196.           fclose(indexfile);
  197.           QFree(newname);
  198.           return FALSE;
  199.         }
  200.       *p++ = dir[i].name[0];
  201.       if (dir[i].name[1] != '_')
  202.         *p++ = dir[i].name[1];
  203. #ifdef QEU_DOS
  204.       *p++ = '\\';
  205. #else
  206.       *p++ = '/';
  207. #endif
  208.       if (dir[i].size == 0)
  209.         {
  210.           fprintf(indexfile, "+ %s *\n", dir[i].name);
  211.           continue;
  212.         }
  213.     }
  214.       if (!strncmp(dir[i].name + 1, "_END", 5)
  215.       || !strncmp(dir[i].name + 2, "_END", 5))
  216.     {
  217.       indir--;
  218.       if (indir < 0)
  219.         {
  220.           fclose(indexfile);
  221.           QFree(newname);
  222.           return FALSE;
  223.         }
  224.       p--;
  225. #ifdef QEU_DOS
  226.       while (*(p - 1) != '\\')
  227.         p--;
  228. #else
  229.       while (*(p - 1) != '/')
  230.         p--;
  231. #endif
  232.       if (dir[i].size == 0)
  233.         {
  234.           fprintf(indexfile, "+ %s *\n", dir[i].name);
  235.           continue;
  236.         }
  237.       continue;
  238.     }
  239.       strcpy(p, dir[i].name);
  240.       if ((dir[i].name[0] == 'E' && dir[i].name[2] == 'M'
  241.        && dir[i].name[4] == '\0')
  242.       || (dir[i].name[0] == 'M' && dir[i].name[1] == 'A'
  243.           && dir[i].name[2] == 'P' && dir[i].name[5] == '\0'))
  244.     {
  245.       inmap = TRUE;
  246.       while (*p)
  247.         p++;
  248. #ifdef QEU_DOS
  249.       *p++ = '\\';
  250. #else
  251.       *p++ = '/';
  252. #endif
  253.       if (dir[i].size > 0)
  254.         strcpy(p, dir[i].name);
  255.       else
  256.         {
  257.           fprintf(indexfile, "+ %s *\n", dir[i].name);
  258.           continue;
  259.         }
  260.     }
  261.       if (entrynum < dirsize && i != entrynum)
  262.     continue; /* horrible trick... */
  263.       strcat(p, ".lmp");
  264.       if (outf != NULL)
  265.     fprintf(outf, "Saving %6ld bytes to %s\n", dir[i].size, newname);
  266.       CreatePathToFile(newname);
  267.       newfile = fopen(newname, "wb");
  268.       if (newfile == NULL)
  269.     {
  270.       fclose(indexfile);
  271.       QFree(newname);
  272.       return FALSE;
  273.     }
  274.       fprintf(indexfile, "+ %s = %s\n", dir[i].name, q);
  275.       if ((fseek(wadfile, offset + dir[i].offset, SEEK_SET) < 0)
  276.       || (CopyBytes(newfile, wadfile, dir[i].size) == FALSE))
  277.     {
  278.       fclose(newfile);
  279.       QFree(newname);
  280.       return FALSE;
  281.     }
  282.       fclose(newfile);
  283.       sum += dir[i].size;
  284.       n++;
  285.     }
  286.   if (outf != NULL && entrynum >= dirsize)
  287.     fprintf(outf, "Saved %lu bytes in %u files.\n", sum, n);
  288.   QFree(newname);
  289.   return TRUE;
  290. }
  291.  
  292.  
  293. /* ----------------------------------------------------------------------------
  294.  * NOTE: How to save a wad file:
  295.  *
  296.  * UInt32    count;  - used by the saving routines
  297.  * WADDirPtr dir;    - WAD directory structure (created step by step)
  298.  * UInt16    n;      - number of entries in WAD directory (idem)
  299.  *
  300.  * WriteWADHeader(f, &count, &dir, &n, TRUE);           - write the header
  301.  * size = WriteSomething(f, ...);                       - save one entry
  302.  * AddWADEntry(f, &count, &dir, &n, name, size);        - add entry to dir.
  303.  * size = WriteSomethingElse(f, ...);                   - save another entry
  304.  * AddWADEntry(f, &count, &dir, &n, othername, size);   - add to dir. too
  305.  * totalsize = WriteWADDirectory(f, &count, dir, n);    - write the directory
  306.  */
  307.  
  308. /*
  309.  * Write the WAD header to the file.  The header will be modified later,
  310.  * when the directory is written to the file.
  311.  * If "patch" is TRUE, the file will be saved as a PWAD.  If "patch" is
  312.  * FALSE, it will be saved as an IWAD.
  313.  */
  314. Bool WriteWADHeader(FILE *wadfile, UInt32 *count_r, WADDirPtr *dir_r,
  315.             UInt16 *dirsize_r, Bool patch)
  316. {
  317.   char buf[100];
  318.  
  319.   sprintf(buf, "PWAD********\r\nQEU %s\r\n", QEU_VERSION);
  320.   if (patch == FALSE)
  321.     buf[0] = 'I';
  322.   if ((wadfile == NULL)
  323.       || (WriteBytes(wadfile, buf, (UInt32)strlen(buf)) == FALSE))
  324.     return FALSE;
  325.   *dir_r = NULL;
  326.   *count_r = (UInt32)strlen(buf);
  327.   *dirsize_r = 0;
  328.   return TRUE;
  329. }
  330.  
  331.  
  332. /*
  333.  * Add a new entry to the WAD directory.  This entry should have been
  334.  * saved previously and be "entrysize" bytes long.  It will be stored
  335.  * in the WAD directory under the name "entryname".
  336.  * All object saving routines in this package return the number of bytes
  337.  * written, so that number can be passed directly to this routine.
  338.  */
  339. Bool AddWADEntry(FILE *wadfile, UInt32 *count_r, WADDirPtr *dir_r,
  340.          UInt16 *dirsize_r, char *entryname, UInt32 entrysize)
  341. {
  342.   UInt16 n;
  343.   char   buf[9];
  344.  
  345.   if (wadfile == NULL || *count_r == 0L)
  346.     return FALSE;
  347.   n = *dirsize_r;
  348.   if (n == 0)
  349.     *dir_r = (WADDirPtr)QMalloc((UInt32)sizeof(struct WADDirectory));
  350.   else
  351.     *dir_r = (WADDirPtr)QRealloc(*dir_r, (UInt32)(n + 1)
  352.                  * (UInt32)sizeof(struct WADDirectory));
  353.   strncpy(buf, entryname, 8);
  354.   buf[8] = '\0';
  355.   QStrNCpy((*dir_r)[n].name, strupr(buf), 8);
  356.   (*dir_r)[n].offset = *count_r;
  357.   (*dir_r)[n].size = entrysize;
  358.   *count_r = *count_r + entrysize;
  359.   *dirsize_r = n + 1;
  360.   return TRUE;
  361. }
  362.  
  363.  
  364. /*
  365.  * Write the WAD directory to the file.  This should only be done
  366.  * after all entries have been saved (using the appropriate
  367.  * Write... routine) and registered (using AddWADEntry).  The WAD
  368.  * header is updated so that it points to the directory.
  369.  *
  370.  * This routine returns the total number of bytes taken by the WAD
  371.  * file (header + all entries + directory).  It is thus possible to
  372.  * include a WAD file in another WAD file or in a PACK file.
  373.  */
  374. UInt32 WriteWADDirectory(FILE *wadfile, UInt32 *count_r, WADDirPtr dir,
  375.              UInt16 dirsize)
  376. {
  377.   UInt32 size, pos;
  378.   UInt16 i;
  379.  
  380.   pos = *count_r;
  381.   if (wadfile == NULL || pos == 0L)
  382.     return 0L;
  383.   *count_r = 0L; /* invalidate the counter */
  384.   size = (UInt32)dirsize;
  385.   if ((fseek(wadfile, 4L - (Int32)(pos), SEEK_CUR) < 0)
  386.       || (WriteInt32(wadfile, &size) == FALSE)
  387.       || (WriteInt32(wadfile, &pos) == FALSE)
  388.       || (fseek(wadfile, (Int32)(pos) - 12L, SEEK_CUR) < 0))
  389.     return 0L;
  390.   for (i = 0; i < dirsize; i++)
  391.     if ((WriteInt32(wadfile, &(dir[i].offset)) == FALSE)
  392.     || (WriteInt32(wadfile, &(dir[i].size)) == FALSE)
  393.     || (WriteBytes(wadfile, &(dir[i].name), 8L) == FALSE))
  394.       return 0L;
  395.   return size * (UInt32)sizeof(struct WADDirectory) + pos;
  396. }
  397.  
  398. /* end of file */
  399.